package org.vaadinpa.data;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.vaadinpa.Conf;
import org.vaadinpa.data.Ship.ShipType;

public class Fleet implements Cloneable {

	public enum FleetOrder {
		ATTACK, DEFEND;

		@Override
		public String toString() {
			switch (this) {
			case ATTACK:
				return "Attack";
			case DEFEND:
				return "Defend";

			default:
				throw new RuntimeException();
			}
		}
	}

	public String name;

	private Map<Ship, Integer> ships = new HashMap<Ship, Integer>();

	public Coordinate location;
	public int eta;
	public FleetOrder currentOrder = FleetOrder.DEFEND;
	public double bonus = 1.0;

	public Fleet(Coordinate location, String name) {
		this.location = location;
		this.name = name;
	}

	public int get(Ship s) {
		Integer val = ships.get(s);
		if (val == null) {
			ships.put(s, 0);
			return 0;
		}
		return val;
	}

	public void put(Ship s, int num) {
		ships.put(s, num);
	}

	public boolean atPlanet(Coordinate coords) {
		return location != null && location.equals(coords);
	}

	/**
	 * Distributes damage of <code>amount</code> between the ships of type
	 * <code>type</code> in this fleet.
	 * 
	 * @return the damage that was left over if all ships were killed.
	 */
	public int takeHit(ShipType type, int amountOfDamageDealt) {

		if (amountOfDamageDealt == 0) {
			return 0;
		}

		System.out.println("dealing " + amountOfDamageDealt + " to class " + type.name());

		int totalNumShipsOfType = 0;
		Map<Ship, Integer> shipToNum = new HashMap<Ship, Integer>();
		for (Ship s : ships.keySet()) {
			if (s.getType() == type) {
				shipToNum.put(s, ships.get(s));
				totalNumShipsOfType += ships.get(s);
			}
		}

		if (totalNumShipsOfType == 0) {
			return 0;
		}

		int damageLeftOver = 0;
		for (Ship s : shipToNum.keySet()) {

			int numberOfShipsInFleet = shipToNum.get(s);
			if (numberOfShipsInFleet == 0) {
				continue;
			}

			int totalDamageToGive = amountOfDamageDealt + damageLeftOver;
			// deal total according to ratio of type 's' to class 'type'
			int damageToThisType = (int) (totalDamageToGive * (double) numberOfShipsInFleet / totalNumShipsOfType);

			System.out.println("Damage to class " + s.getName() + ": " + damageToThisType);

			int killedShips = damageToThisType / s.getArmor();
			damageLeftOver = damageToThisType % s.getArmor();

			System.out.println("destroyed " + killedShips);
			if (killedShips > ships.get(s)) {
				ships.put(s, 0);
			} else {
				ships.put(s, ships.get(s) - killedShips);
			}
		}

		return damageLeftOver;
	}

	/**
	 * Calculates the total of damage that this fleet produces during the
	 * specified init
	 */
	public int fire(int init) {
		int damage = 0;
		Random r = new Random();

		if (currentOrder == FleetOrder.DEFEND) {
			bonus = Conf.FLEET_DEFEND_MULTI;
		}
		for (Ship s : ships.keySet()) {
			if (s.getInit() == init) {

				// not all shots hit, each ship tries in turn
				for (int shipNum = 0; shipNum < ships.get(s); shipNum++) {
					if (r.nextDouble() < Conf.COMBAT_SHOT_PROPABILITY) {
						damage += s.getPower() * bonus;
					}
				}
			}
		}
		return damage;
	}

	public void addShips(Ship type, int num) {
		Integer existing = ships.get(type);
		if (existing == null) {
			existing = 0;
		}
		ships.put(type, existing + num);
	}

	public boolean isEmpty() {
		for (Integer i : ships.values()) {
			if (i > 0) {
				return false;
			}
		}
		return true;
	}

	@Override
	public Fleet clone() {

		Fleet clone;
		try {
			clone = (Fleet) super.clone();

			clone.location = new Coordinate(location.x, location.y);

			clone.ships = new HashMap<Ship, Integer>();
			for (Entry<Ship, Integer> e : ships.entrySet()) {
				clone.ships.put(e.getKey(), e.getValue());
			}

			return clone;

		} catch (CloneNotSupportedException e1) {
			e1.printStackTrace();
		}

		return null;
	}

	public int numShips() {
		int i = 0;
		for (Integer in : ships.values()) {
			i += in;
		}
		return i;
	}

	public void serialize(StringBuilder sb) {
		sb.append("<fleet>\n");
		sb.append("<name>" + name + "</name>\n");
		sb.append("<bonus>" + bonus + "</bonus>\n");
		sb.append("<currentOrder>" + currentOrder + "</currentOrder>\n");
		sb.append("<eta>" + eta + "</eta>\n");
		if (location != null) {
			sb.append("<coords x=\"" + location.x + "\" y=\"" + location.y + "\"/>\n");
		}
		for (Entry<Ship, Integer> e : ships.entrySet()) {
			sb.append("<ship type=\"" + e.getKey().getName() + "\" num=\"" + e.getValue() + "\"/>\n");
		}
		sb.append("</fleet>\n");

	}
}
